package com.jzsm.modules.sender.senderService.impl.sms;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.jzsm.modules.sender.bo.accountConfig.SmsHuaWeiCloudAccountConfigBo;
import com.jzsm.modules.sender.enums.ChannelTypeEnum;
import com.jzsm.common.annotation.Channel;
import com.jzsm.common.utils.AssertUtils;
import com.jzsm.common.utils.StringUtils;
import com.jzsm.modules.sender.pojo.PushChannelAccount;
import com.jzsm.modules.sender.senderService.AbstractMessageSender;
import com.jzsm.modules.sender.vo.SmsMessageVo;
import lombok.AccessLevel;
import lombok.experimental.FieldDefaults;
import org.apache.http.entity.ContentType;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 华为云 短信渠道
 */
@Component
@Channel(ChannelTypeEnum.SMSHuaWeiCloud)
@FieldDefaults(level = AccessLevel.PRIVATE)
public class SMSHuaWeiCloudMessageSender extends AbstractMessageSender {
    static final String SUCCES_CODE = "000000";

    /**
     * 无需修改,用于格式化鉴权头域,给"X-WSSE"参数赋值
     */
    static final String WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";

    /**
     * 无需修改,用于格式化鉴权头域,给"Authorization"参数赋值
     */
    static final String AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"";

    /**
     * 华为云url
     */
    static final String PREFIX_URL = "https://rtcsms.cn-north-1.myhuaweicloud.com:10743";

    @Override
    public String sendMessage(String message, PushChannelAccount account) {
        return run(message, account);
    }

    /**
     * @param message
     * @param account
     */
    public String run(String message, PushChannelAccount account) {
        final String url = PREFIX_URL + "/sms/batchSendSms/v1";
        TypeReference<SmsMessageVo<String[]>> typeReference = new TypeReference<SmsMessageVo<String[]>>() {
        };
        SmsMessageVo<String[]> smsMessageVo = JSON.parseObject(message, typeReference);
        String accountConfig = account.getAccountConfig();
        SmsHuaWeiCloudAccountConfigBo configBo = JSON.parseObject(accountConfig, SmsHuaWeiCloudAccountConfigBo.class);
        checkAccountConfig(configBo);
        String templateId = StringUtils.isNotBlank(configBo.getTemplateId()) ? configBo.getTemplateId() : smsMessageVo.getTemplateId(); //模板ID

        //必填,全局号码格式(包含国家码),示例:+8615123456789,多个号码之间用英文逗号分隔
        String receiver = smsMessageVo.getPhoneNum(); //短信接收人号码
        AssertUtils.isNotBlank("phoneNum 接收人参数不允许为空！参数实例 +8613800138000", receiver);


        /**
         * 选填,使用无变量模板时请赋空值 String templateParas = "";
         * 单变量模板示例:模板内容为"您的验证码是${1}"时,templateParas可填写为"[\"369751\"]"
         * 双变量模板示例:模板内容为"您有${1}件快递请到${2}领取"时,templateParas可填写为"[\"3\",\"人民公园正门\"]"
         * 模板中的每个变量都必须赋值，且取值不能为空
         * 查看更多模板和变量规范:产品介绍>模板和变量规范
         */
        //模板变量，此处以单变量验证码短信为例，请客户自行生成6位验证码，并定义为字符串类型，以杜绝首位0丢失的问题（例如：002569变成了2569）。
        String[] params = smsMessageVo.getParams();
        AssertUtils.isFalse("模板参数不允许为空！参数类型 String[]", params == null || params.length == 0);
        String templateParas = JSON.toJSONString(params);

        //请求Body,不携带签名名称时,signature请填null
        String body = buildRequestBody(configBo.getSender(), receiver, templateId, templateParas, configBo.getStatusCallBack(), configBo.getSignature());
        AssertUtils.isNotBlank("请求body为空！", body);

        //请求Headers中的X-WSSE参数值
        String wsseHeader = buildWsseHeader(configBo.getAppKey(), configBo.getAppSecret());
        AssertUtils.isNotBlank("请求头X-WSSE参数为空！", wsseHeader);

        // 设置请求头
        HashMap<String, String> headersMap = new HashMap<>();
        headersMap.put("Content-Type", ContentType.APPLICATION_FORM_URLENCODED.getMimeType());
        headersMap.put("Authorization", AUTH_HEADER_VALUE);
        headersMap.put("X-WSSE", wsseHeader);

        HttpResponse httpResponse = HttpRequest.post(url).addHeaders(headersMap).body(body).execute();
        AssertUtils.isTrue(String.format("华为云短信发送错误返回结果：%s", httpResponse.body()), httpResponse.getStatus() == HttpURLConnection.HTTP_OK);
        String httpResponseBody = httpResponse.body();
        Map resMap = JSON.parseObject(httpResponseBody, Map.class);
        boolean isSuccess = StringUtils.equals(SUCCES_CODE, StringUtils.toString(resMap.get("code")));
        AssertUtils.isTrue(httpResponseBody, isSuccess);
        return httpResponseBody;
    }

    /**
     * @param sender
     * @param receiver
     * @param templateId
     * @param templateParas
     * @param statusCallBack
     * @param signature
     * @return
     */
    static String buildRequestBody(String sender, String receiver, String templateId, String templateParas,
                                   String statusCallBack, String signature) {
        if (null == sender || null == receiver || null == templateId || sender.isEmpty() || receiver.isEmpty()
                || templateId.isEmpty()) {
            return null;
        }
        Map<String, String> map = new HashMap<>();

        map.put("from", sender);
        map.put("to", receiver);
        map.put("templateId", templateId);
        if (null != templateParas && !templateParas.isEmpty()) {
            map.put("templateParas", templateParas);
        }
        if (null != statusCallBack && !statusCallBack.isEmpty()) {
            map.put("statusCallback", statusCallBack);
        }
        if (null != signature && !signature.isEmpty()) {
            map.put("signature", signature);
        }

        StringBuilder sb = new StringBuilder();
        String temp = "";

        for (String s : map.keySet()) {
            try {
                temp = URLEncoder.encode(map.get(s), "UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            sb.append(s).append("=").append(temp).append("&");
        }

        return sb.deleteCharAt(sb.length() - 1).toString();
    }

    /**
     * 构造X-WSSE参数值
     *
     * @param appKey
     * @param appSecret
     * @return
     */
    static String buildWsseHeader(String appKey, String appSecret) {
        if (null == appKey || null == appSecret || appKey.isEmpty() || appSecret.isEmpty()) {
            return null;
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        String time = sdf.format(new Date()); //Created
        String nonce = UUID.randomUUID().toString().replace("-", ""); //Nonce

        MessageDigest md;
        byte[] passwordDigest = null;

        try {
            md = MessageDigest.getInstance("SHA-256");
            md.update((nonce + time + appSecret).getBytes());
            passwordDigest = md.digest();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        //如果JDK版本是1.8,请加载原生Base64类,并使用如下代码
        String passwordDigestBase64Str = Base64.getEncoder().encodeToString(passwordDigest); //PasswordDigest
        //如果JDK版本低于1.8,请加载三方库提供Base64类,并使用如下代码
        //String passwordDigestBase64Str = Base64.encodeBase64String(passwordDigest); //PasswordDigest
        //若passwordDigestBase64Str中包含换行符,请执行如下代码进行修正
        //passwordDigestBase64Str = passwordDigestBase64Str.replaceAll("[\\s*\t\n\r]", "");
        return String.format(WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, time);
    }

    /**
     * 校验配置
     *
     * @param configBo
     */
    private void checkAccountConfig(SmsHuaWeiCloudAccountConfigBo configBo) {
        AssertUtils.isNotBlank("appKey 不允许为空！", configBo.getAppKey());
        AssertUtils.isNotBlank("appSecret 不允许为空！", configBo.getAppSecret());
        AssertUtils.isNotBlank("sender 不允许为空！", configBo.getSender());
        AssertUtils.isNotBlank("templateId 不允许为空！", configBo.getTemplateId());
    }
}
